﻿/***************************************************************
 * Name:      http_worker.cpp
 * Purpose:   HTTP工作线程类
 * Author:    杨卓荦 (yzl@bang5.net)
 * Created:   2010-08-29
 * Copyright: 杨卓荦 (http://www.bang5.net)
 * License:
 **************************************************************/

#include "http_worker.h"
#include "url_parser.h"
using namespace std;

// 设定static成员变量初值时，不受任何存取权限(private)的束缚
wxCriticalSection HttpWorker::file_section_;

HttpWorker::HttpWorker(const string& url,
                       const int retry,
                       const int retry_wait,
                       const long long int from,
                       const long long int file_size,
                       const string& referer,
                       const string& local_file_name) {
  retry_ = retry;
  retry_wait_ = retry_wait;
  from_ = from;
  file_size_ = file_size;
  local_file_name_ = local_file_name;

  UrlParser url_parser;
  url_parser.SetUrl(url);
  url_ = url_parser.GetRawUrl();
  file_ = url_parser.GetEscFilePathName();
  host_ = url_parser.GetServer();
  server_ = url_parser.GetServer();
  port_ = url_parser.GetPort();
  referer_ = referer.empty() ? url_parser.GetRefer() : referer;
  total_byte_ = 0;  // 共接收了total_byte_个字节
}

HttpWorker::~HttpWorker() {
}

void* HttpWorker::Entry() {
  const int kSocketConnectError = -1;  // 套接字连接错误
  const int kGetFileError = -2;  // 获得文件失败
  const int kGetDataError = -3;  // 获得数据失败
  while (true) {
    try {
      if (!Connect(server_, port_)) throw kSocketConnectError;
      if (!MoveToFileContent(file_.c_str(),
                             from_ + total_byte_,
                             referer_.c_str())) throw kGetFileError;
      // 从第 from_ + total_byte_ 字节开始接收文件数据
      if (GetData(from_ + total_byte_) < 0) throw kGetDataError;
    } catch (int error) {
      if (Retry()) continue;
      else break;
    }
    Close();
    break;
  }
  return NULL;
}


//发送请求文件命令
bool HttpWorker::MoveToFileContent(const char* file_name,
                         const long long int pos,
                         const char* referer) {
  if (SendHttpGetRequest(host_.c_str(), file_name, pos, referer)) {
    return GetHead();
  }
  return false;
}

int HttpWorker::GetData(long long int start_pos) {
  const int kBufferSize = 2048;
  char buffer[kBufferSize];
  int read_from_socket = 0;
  int result = 0;
  while (true) {
    read_from_socket = Read(buffer, kBufferSize);
    if (read_from_socket > 0) {
      // 写入 read_from_socket个数据
      result = WriteData(start_pos, read_from_socket, buffer);
      if (result < 0) break;  // 写入异常
      start_pos += read_from_socket;
      total_byte_ += read_from_socket;
      if (start_pos >= file_size_) break;  // 写入足够多的数据
    } else {
      break;
    }
  }
  return result;
}

bool HttpWorker::GetHead() {
  char buffer[1024];  // HTTP 头最大长度为 1024
  int pos = 0;  // 当接收前指针的位置
  int received = 0;  // 已经接收的字节数

  // 先收4个字节
  if ((received = Read(buffer, 4) ) <= 0) return false;
  pos += received;

  // HTTP 头以 "\r\n\r\n" 结尾，内容从pos开始
  while (!(buffer[pos - 1] == '\n' && buffer[pos - 2] == '\r' &&
           buffer[pos - 3] == '\n' && buffer[pos - 4] == '\r') && pos < 1022) {
    if ((received = Read(buffer + pos, 1)) <= 0) {
      return false;
    } else {
      pos += received;
    }
  }

  if (pos >= 1022) return false;

  buffer[pos] = 0;
  int offset = 0;
  char line[ 256 ];
  int linebyte;
  int ret_code = -1;

  // 分析内容，得到状态码
  while ((linebyte = GetBufLine(buffer + offset, pos, line)) > 0) {
    offset += linebyte;
    if (strncasecmp(line, "HTTP/1.", 7) == 0) {
      char ret_code_str[4];
      memcpy(ret_code_str, line + 9, 3);
      ret_code_str[3] = 0;
      ret_code = atoi(ret_code_str);
    }
  }

  if (ret_code / 100 != 2) { // HTTP 状态错误，必须是200、206之类
    return false;
  }
  return true;
}

bool HttpWorker::Retry() {
  if ( --retry_ > 0 ) {
    // 等待 retry_wait_ 秒后重试
#ifdef WIN32
    Sleep(1000 * retry_wait_);
#else
    sleep(retry_wait_);
#endif
    return true;
  }
  return false;
}

int HttpWorker::WriteData(const long long int offset,
                          const unsigned int length,
                          const char *buffer) {
  const int kFileOpenError = -1;  // 文件打开错误
  const int kFileSeekError = -2;  // 文件指针位置错误
  const int kFileWriteError = -3;  // 文件写入错误
  // 声明这个函数为临界区域
  wxCriticalSectionLocker locker(file_section_);
  FILE* file = fopen(local_file_name_.c_str(), "rb+");
  if (NULL == file) {
    file = fopen(local_file_name_.c_str(), "wb");
  }
  if (NULL == file) return kFileOpenError;
  if (0 != fseek(file, offset, SEEK_SET)) return kFileSeekError;
  if (length != fwrite(buffer, 1, length, file)) return kFileWriteError;
  fclose(file);
  return 0;
}
